﻿/*
-----------------------------------------------------------------------------
This source file is part of Cell Cloud.

Copyright (c) 2009-2012 Cell Cloud Team - cellcloudproject@gmail.com

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-----------------------------------------------------------------------------
*/

#include "CellStableHeaders.h"
#include "CellPacket.h"
#include "CellException.h"
#include "CellUtil.h"

namespace cell
{

//-----------------------------------------------------------------------
Packet::Packet(void)
	: mBody(NULL)
	, mBodyLength(0)
	, mBodyBuffLength(0)
	, mSegmentNum(0)
{
	memset(mTag, 0x0, sizeof(mTag));
	memset(mVersion, 0x0, sizeof(mVersion));
	memset(mSequenceNumber, 0x0, sizeof(mSequenceNumber));
	memset(mBodyLengthSegment, 0x0, sizeof(mBodyLengthSegment));

	memcpy(mVersion, "0001", PSL_VERSION);
	memcpy(mSequenceNumber, "0000", PSL_SN);

	char len[PSL_BODYLENGTH_STRINGLENGTH] = {0x0};
	formatNumber(len, 0, PSL_BODY_LENGTH);
	memcpy(mBodyLengthSegment, len, PSL_BODY_LENGTH);
}
//-----------------------------------------------------------------------
Packet::Packet(char* tag, uint16 sn, uint16 major, uint16 minor)
	: mBody(NULL)
	, mBodyLength(0)
	, mBodyBuffLength(0)
	, mSegmentNum(0)
{
	memset(mTag, 0x0, sizeof(mTag));
	memset(mVersion, 0x0, sizeof(mVersion));
	memset(mSequenceNumber, 0x0, sizeof(mSequenceNumber));
	memset(mBodyLengthSegment, 0x0, sizeof(mBodyLengthSegment));

	setTag(tag);
	setVersion(major, minor);
	setSequenceNumber(sn);

	char len[PSL_BODYLENGTH_STRINGLENGTH] = {0x0};
	formatNumber(len, 0, PSL_BODY_LENGTH);
	memcpy(mBodyLengthSegment, len, PSL_BODY_LENGTH);
}
//-----------------------------------------------------------------------
Packet::Packet(byte* tag, uint16 sn, uint16 major, uint16 minor)
	: mBody(NULL)
	, mBodyLength(0)
	, mBodyBuffLength(0)
	, mSegmentNum(0)
{
	memset(mTag, 0x0, sizeof(mTag));
	memset(mVersion, 0x0, sizeof(mVersion));
	memset(mSequenceNumber, 0x0, sizeof(mSequenceNumber));
	memset(mBodyLengthSegment, 0x0, sizeof(mBodyLengthSegment));

	setTag(tag);
	setVersion(major, minor);
	setSequenceNumber(sn);

	char len[PSL_BODYLENGTH_STRINGLENGTH] = {0x0};
	formatNumber(len, 0, PSL_BODY_LENGTH);
	memcpy(mBodyLengthSegment, len, PSL_BODY_LENGTH);
}
//-----------------------------------------------------------------------
Packet::Packet(char* tag, uint16 sn, char* body, uint length, uint16 major, uint16 minor)
	: mBody(NULL)
	, mBodyLength(0)
	, mBodyBuffLength(0)
	, mSegmentNum(0)
{
	memset(mTag, 0x0, sizeof(mTag));
	memset(mVersion, 0x0, sizeof(mVersion));
	memset(mSequenceNumber, 0x0, sizeof(mSequenceNumber));
	memset(mBodyLengthSegment, 0x0, sizeof(mBodyLengthSegment));

	setTag(tag);
	setVersion(major, minor);
	setSequenceNumber(sn);
	setBody(body, length);
}
//-----------------------------------------------------------------------
Packet::~Packet(void)
{
	if (NULL != mBody)
	{
		CELL_FREE(mBody, MEMCATEGORY_GENERAL);
	}
}
//-----------------------------------------------------------------------
uint16 Packet::getTag(char* tag) const
{
	memcpy(tag, mTag, PSL_TAG);
	return PSL_TAG;
}
//-----------------------------------------------------------------------
uint16 Packet::getTag(byte* tag) const
{
	memcpy(tag, mTag, PSL_TAG);
	return PSL_TAG;
}
//-----------------------------------------------------------------------
void Packet::setTag(const char* tag)
{
	memcpy(mTag, tag, PSL_TAG);
}
//-----------------------------------------------------------------------
void Packet::setTag(const byte* tag)
{
	memcpy(mTag, tag, PSL_TAG);
}
//-----------------------------------------------------------------------
bool Packet::compareTag(const char* tag) const
{
	for (uint16 i = 0; i < PSL_TAG; ++i)
	{
		if (mTag[i] != tag[i])
			return false;
	}
	return true;
}
//-----------------------------------------------------------------------
bool Packet::compareTag(const byte* tag) const
{
	for (uint16 i = 0; i < PSL_TAG; ++i)
	{
		if (mTag[i] != tag[i])
			return false;
	}
	return true;
}
//-----------------------------------------------------------------------
void Packet::getVersion(uint16* major, uint16* minor) const
{
	char buf[3] = {0x0};
	memcpy(buf, mVersion, 2);
	*minor = atoi(buf);

	char version[PSL_VERSION + 1] = {0x0};
	memcpy(version, mVersion, PSL_VERSION);
	char* pbuf = version;
	pbuf += 2;
	memset(buf, 0x0, sizeof(buf));
	memcpy(buf, pbuf, 2);
	*major = atoi(buf);
}
//-----------------------------------------------------------------------
void Packet::setVersion(uint16 major, uint16 minor)
{
	char buf[PSL_VERSION + 1] = {0x0};
	sprintf(buf, "%02d%02d", minor, major);
	memcpy(mVersion, buf, PSL_VERSION);
}
//-----------------------------------------------------------------------
void Packet::getSequenceNumber(uint16* sn) const
{
	char buf[PSL_SN + 1] = {0x0};
	memcpy(buf, mSequenceNumber, PSL_SN);
	*sn = atoi(buf);
}
//-----------------------------------------------------------------------
void Packet::setSequenceNumber(uint16 sn)
{
	char buf[PSL_SN + 1] = {0x0};
	if (sn > 9999)
		sn = 9999;

	// 格式化数字
	formatNumber(buf, sn, PSL_SN);

	memcpy(mSequenceNumber, buf, PSL_SN);
}
//-----------------------------------------------------------------------
uint Packet::getBody(byte* body) const
{
	if (mBody == NULL)
		return mBodyLength;

	memcpy(body, mBody, mBodyLength);
	return mBodyLength;
}
//-----------------------------------------------------------------------
uint Packet::getBody(char* body) const
{
	if (mBody == NULL)
		return mBodyLength;

	memcpy(body, mBody, mBodyLength);
	return mBodyLength;
}
//-----------------------------------------------------------------------
inline uint Packet::getBodyLength(void) const
{
	return mBodyLength;
}
//-----------------------------------------------------------------------
uint Packet::setBody(const byte* body, const uint len)
{
	// 判断数据长度
	if (len == 0)
	{
		if (NULL != mBody)
		{
			CELL_FREE(mBody, MEMCATEGORY_GENERAL);
			mBody = NULL;
		}
		mBodyLength = 0;
		return mBodyLength;
	}

	uint dataLen = len;
	if (dataLen > PACKET_MAX_BODYLENGTH)
		dataLen = PACKET_MAX_BODYLENGTH;

	if (NULL != mBody)
	{
		CELL_FREE(mBody, MEMCATEGORY_GENERAL);
		mBody = NULL;
	}
	mBody = CELL_ALLOC_T(char, dataLen + 1, MEMCATEGORY_GENERAL);
	memset(mBody, 0x0, dataLen + 1);
	mBodyLength = dataLen;

	// 复制数据
	memcpy(mBody, body, dataLen);

	// 格式化数据长度
	char buf[PSL_BODYLENGTH_STRINGLENGTH] = {0x0};
	formatNumber(buf, dataLen, PSL_BODY_LENGTH);
	// 填充数据
	memcpy(mBodyLengthSegment, buf, PSL_BODY_LENGTH);

	return mBodyLength;
}
//-----------------------------------------------------------------------
uint Packet::setBody(const char* body, const uint len)
{
	// 判断数据长度
	if (len == 0)
	{
		if (NULL != mBody)
		{
			CELL_FREE(mBody, MEMCATEGORY_GENERAL);
			mBody = NULL;
		}
		mBodyLength = 0;
		return 0;
	}

	uint dataLen = len;
	if (dataLen > PACKET_MAX_BODYLENGTH)
		dataLen = PACKET_MAX_BODYLENGTH;

	if (NULL != mBody)
	{
		CELL_FREE(mBody, MEMCATEGORY_GENERAL);
		mBody = NULL;
	}
	mBody = CELL_ALLOC_T(char, dataLen + 1, MEMCATEGORY_GENERAL);
	memset(mBody, 0x0, dataLen + 1);
	mBodyLength = dataLen;

	// 复制数据
	memcpy(mBody, body, dataLen);

	// 格式化数据长度
	char buf[PSL_BODYLENGTH_STRINGLENGTH] = {0x0};
	formatNumber(buf, dataLen, PSL_BODY_LENGTH);
	// 填充数据
	memcpy(mBodyLengthSegment, buf, PSL_BODY_LENGTH);

	return mBodyLength;
}
//-----------------------------------------------------------------------
uint Packet::getSubsegmentNum(void) const
{
	if (0 == mSegmentNum)
	{
		if (NULL != mBody && mBodyLength > PSL_SUBSEGMENT_NUM)
		{
			char buf[PSL_SUBSEGMENTNUM_STRINGLENGTH] = {0x0};
			memcpy(buf, mBody, PSL_SUBSEGMENT_NUM);
			mSegmentNum = atoi(buf);
			return mSegmentNum;
		}
	}

	return mSegmentNum;
}
//-----------------------------------------------------------------------
uint Packet::getSubsegmentLength(const uint index) const
{
	if (getSubsegmentNum() == 0 || (int)(getSubsegmentNum() - 1) < (int)index)
	{
		// 索引越界
		return 0;
	}

	// 定位游标
	uint cursor = PSL_SUBSEGMENT_NUM;
	cursor += index * PSL_SUBSEGMENT_LENGTH;

	char buf[PSL_SUBSEGMENTLENGTH_STRINGLENGTH] = {0x0};
	memcpy(buf, mBody + cursor, PSL_SUBSEGMENT_LENGTH);
	uint length = atoi(buf);
	return length;
}
//-----------------------------------------------------------------------
uint Packet::getSubsegment(char* data, const uint index) const
{
	uint len = getSubsegmentLength(index);
	if (len == 0)
	{
		return 0;
	}

	uint num = getSubsegmentNum();
	// 定位游标
	uint cursor = PSL_SUBSEGMENT_NUM;
	// 跳过长度描述段
	cursor += num * PSL_SUBSEGMENT_LENGTH;
	// 依次跳过 index 之前的数据段
	for (uint i = 0; i < index; ++i)
	{
		cursor += getSubsegmentLength(i);
	}

	memcpy(data, mBody + cursor, len);
	return len;
}
//-----------------------------------------------------------------------
uint Packet::getSubsegment(byte* data, const uint index) const
{
	uint len = getSubsegmentLength(index);
	if (len == 0)
	{
		return 0;
	}

	uint num = getSubsegmentNum();
	// 定位游标
	uint cursor = PSL_SUBSEGMENT_NUM;
	// 跳过长度描述段
	cursor += num * PSL_SUBSEGMENT_LENGTH;
	// 依次跳过 index 之前的数据段
	for (uint i = 0; i < index; ++i)
	{
		cursor += getSubsegmentLength(i);
	}

	memcpy(data, mBody + cursor, len);
	return len;
}
//-----------------------------------------------------------------------
uint Packet::appendSubsegment(const byte* data, const uint len)
{
	char* buf = CELL_ALLOC_T(char, len + 1, MEMCATEGORY_GENERAL);
	memset(buf, 0x0, len + 1);
	memcpy(buf, data, len);
	uint index = appendSubsegment(buf, len);
	CELL_FREE(buf, MEMCATEGORY_GENERAL);
	return index;
}
//-----------------------------------------------------------------------
uint Packet::appendSubsegment(const char* data, const uint len)
{
	if (NULL != mBody)
	{
		uint num = getSubsegmentNum();
		// 子段数据描述段长度
		uint subdesclen = num * PSL_SUBSEGMENT_LENGTH;
		// 子段数据区域的整体长度
		uint subdatalen = mBodyLength - PSL_SUBSEGMENT_NUM - subdesclen;

		// 数据描述段数据
		char* lenbuf = CELL_ALLOC_T(char, subdesclen + 1, MEMCATEGORY_GENERAL);
		memset(lenbuf, 0x0, subdesclen + 1);
		memcpy(lenbuf, mBody + PSL_SUBSEGMENT_NUM, subdesclen);
		// 数据段数据
		char* databuf = CELL_ALLOC_T(char, subdatalen + 1, MEMCATEGORY_GENERAL);
		memset(databuf, 0x0, subdatalen + 1);
		memcpy(databuf, mBody + PSL_SUBSEGMENT_NUM + subdesclen, subdatalen);

		// 更新数量
		num += 1;
		++mSegmentNum;

		// 计算 Body 长度
		mBodyLength = PSL_SUBSEGMENT_NUM + (num * PSL_SUBSEGMENT_LENGTH) + subdatalen + len;

		// 如果 Body Length 大于当前 Buff Size 则重新分配内存空间
		if (mBodyLength > mBodyBuffLength)
		{
			// 清空数据域
			CELL_FREE(mBody, MEMCATEGORY_GENERAL);
			mBodyBuffLength = resolveBuffSize(mBodyBuffLength, mBodyLength);

			// 重新分配内存
			mBody = CELL_ALLOC_T(char, mBodyBuffLength + 1, MEMCATEGORY_GENERAL);
			memset(mBody, 0x0, mBodyBuffLength + 1);
		}

		uint cursor = 0;

		// 新子段数量
		formatNumber(mBody + cursor, num, PSL_SUBSEGMENT_NUM);
		cursor += PSL_SUBSEGMENT_NUM;
		// 原数据长度描述
		memcpy(mBody + cursor, lenbuf, subdesclen);
		cursor += subdesclen;
		// 新数据长度描述
		formatNumber(mBody + cursor, len, PSL_SUBSEGMENT_LENGTH);
		//memcpy(mBody + cursor, numbuf, PSL_SUBSEGMENT_LENGTH);
		cursor += PSL_SUBSEGMENT_LENGTH;
		// 原数据内容
		memcpy(mBody + cursor, databuf, subdatalen);
		cursor += subdatalen;
		// 新数据内容
		memcpy(mBody + cursor, data, len);

		// Body 长度段
		char lenstr[PSL_BODYLENGTH_STRINGLENGTH] = {0x0};
		formatNumber(lenstr, mBodyLength, PSL_BODY_LENGTH);
		memcpy(mBodyLengthSegment, lenstr, PSL_BODY_LENGTH);

		CELL_FREE(databuf, MEMCATEGORY_GENERAL);
		CELL_FREE(lenbuf, MEMCATEGORY_GENERAL);

		return num - 1;
	}
	else
	{
		uint totalLen = PSL_SUBSEGMENT_NUM + PSL_SUBSEGMENT_LENGTH + len;

		// 分配一个较大的存储空间
		mBodyBuffLength = Util::max(2048, totalLen);

		mBody = CELL_ALLOC_T(char, mBodyBuffLength + 1, MEMCATEGORY_GENERAL);
		memset(mBody, 0x0, mBodyBuffLength + 1);
		mBodyLength = totalLen;

		uint cursor = 0;

		// 子段数量
		formatNumber(mBody + cursor, 1, PSL_SUBSEGMENT_NUM);
		cursor += PSL_SUBSEGMENT_NUM;

		// 第一个子段长度
		formatNumber(mBody + cursor, len, PSL_SUBSEGMENT_LENGTH);
		cursor += PSL_SUBSEGMENT_LENGTH;

		// 子段数据
		memcpy(mBody + cursor, data, len);

		// Body 长度段
		char lenstr[PSL_BODYLENGTH_STRINGLENGTH] = {0x0};
		formatNumber(lenstr, mBodyLength, PSL_BODY_LENGTH);
		memcpy(mBodyLengthSegment, lenstr, PSL_BODY_LENGTH);

		// 更新数量
		++mSegmentNum;

		return 0;
	}
}
//-----------------------------------------------------------------------
void Packet::removeSubsegment(const uint index)
{
	if (!mBody)
		return;

	// TODO
	//uint num = getSubsegmentNum();
}
//-----------------------------------------------------------------------
void Packet::formatNumber(char* output, uint num, uint numlen)
{
	switch (numlen)
	{
	case 4:
	{
		sprintf(output, "%04d", num);
		break;
	}
	case 8:
	{
		sprintf(output, "%08d", num);
		break;
	}
	default:
		break;
	}
}
//-----------------------------------------------------------------------
uint Packet::resolveBuffSize(const uint currentSize, const uint minimumSize)
{
	uint mod = currentSize % 1024;
	uint clipping = currentSize - mod;
	uint ret = currentSize;
	if (clipping < 8192)
		ret += clipping * 0.5;
	else
		ret += clipping * 0.25;

	if (ret < minimumSize)
		ret = minimumSize;

	if (ret > PACKET_MAX_BODYLENGTH)
		ret = PACKET_MAX_BODYLENGTH;

	return ret;
}

//-----------------------------------------------------------------------
uint Packet::pack(char* output, const Packet* input)
{
	uint cursor = 0;
	// tag
	memcpy(output + cursor, input->mTag, PSL_TAG);
	cursor += PSL_TAG;

	// version
	memcpy(output + cursor, input->mVersion, PSL_VERSION);
	cursor += PSL_VERSION;

	// sequence number
	memcpy(output + cursor, input->mSequenceNumber, PSL_SN);
	cursor += PSL_SN;

	// body length segment
	memcpy(output + cursor, input->mBodyLengthSegment, PSL_BODY_LENGTH);
	cursor += PSL_BODY_LENGTH;

	// body
	if (input->mBodyLength > 0)
	{
		memcpy(output + cursor, input->mBody, input->mBodyLength);
		cursor += input->mBodyLength;
	}

	return cursor;
}
//-----------------------------------------------------------------------
uint Packet::pack(byte* output, const Packet* input)
{
	uint cursor = 0;
	// tag
	memcpy(output + cursor, input->mTag, PSL_TAG);
	cursor += PSL_TAG;

	// version
	memcpy(output + cursor, input->mVersion, PSL_VERSION);
	cursor += PSL_VERSION;

	// sequence number
	memcpy(output + cursor, input->mSequenceNumber, PSL_SN);
	cursor += PSL_SN;

	// body length segment
	memcpy(output + cursor, input->mBodyLengthSegment, PSL_BODY_LENGTH);
	cursor += PSL_BODY_LENGTH;

	// body
	if (input->mBodyLength > 0)
	{
		memcpy(output + cursor, input->mBody, input->mBodyLength);
		cursor += input->mBodyLength;
	}

	return cursor;
}
//-----------------------------------------------------------------------
bool Packet::unpack(Packet* output, const char* input, const uint len)
{
	char* data = CELL_ALLOC_T(char, len + 1, MEMCATEGORY_GENERAL);
	memset(data, 0x0, len + 1);
	memcpy(data, input, len);

	char* pdata = data;
	uint cursor = 0;

	// Tag
	memcpy(output->mTag, pdata + cursor, PSL_TAG);
	cursor += PSL_TAG;

	if (cursor > len)
	{
		CELL_FREE(data, MEMCATEGORY_GENERAL);
		return false;
	}

	// Version
	memcpy(output->mVersion, pdata + cursor, PSL_VERSION);
	cursor += PSL_VERSION;

	if (cursor > len)
	{
		CELL_FREE(data, MEMCATEGORY_GENERAL);
		return false;
	}

	// Sequence number
	memcpy(output->mSequenceNumber, pdata + cursor, PSL_SN);
	cursor += PSL_SN;

	if (cursor > len)
	{
		CELL_FREE(data, MEMCATEGORY_GENERAL);
		return false;
	}

	// Body length segment
	memcpy(output->mBodyLengthSegment, pdata + cursor, PSL_BODY_LENGTH);
	cursor += PSL_BODY_LENGTH;

	if (cursor == len)
	{
		// 游标和数据长度相等，数据包没有 BODY 段。
		CELL_FREE(data, MEMCATEGORY_GENERAL);
		return true;
	}

	if (cursor > len)
	{
		CELL_FREE(data, MEMCATEGORY_GENERAL);
		return false;
	}

	// Body
	output->setBody(pdata + cursor, len - cursor);

	CELL_FREE(data, MEMCATEGORY_GENERAL);

	return true;
}
//-----------------------------------------------------------------------
bool Packet::unpack(Packet* output, const byte* input, const uint len)
{
	byte* data = CELL_ALLOC_T(byte, len, MEMCATEGORY_GENERAL);
	memcpy(data, input, len);

	byte* pdata = data;
	uint cursor = 0;

	// Tag
	memcpy(output->mTag, pdata + cursor, PSL_TAG);
	cursor += PSL_TAG;

	if (cursor > len)
	{
		CELL_FREE(data, MEMCATEGORY_GENERAL);
		return false;
	}

	// Version
	memcpy(output->mVersion, pdata + cursor, PSL_VERSION);
	cursor += PSL_VERSION;

	if (cursor > len)
	{
		CELL_FREE(data, MEMCATEGORY_GENERAL);
		return false;
	}

	// Sequence number
	memcpy(output->mSequenceNumber, pdata + cursor, PSL_SN);
	cursor += PSL_SN;

	if (cursor > len)
	{
		CELL_FREE(data, MEMCATEGORY_GENERAL);
		return false;
	}

	// Body length segment
	memcpy(output->mBodyLengthSegment, pdata + cursor, PSL_BODY_LENGTH);
	cursor += PSL_BODY_LENGTH;

	if (cursor == len)
	{
		// 游标和数据长度相等，数据包没有 BODY 段。
		CELL_FREE(data, MEMCATEGORY_GENERAL);
		return true;
	}

	if (cursor > len)
	{
		CELL_FREE(data, MEMCATEGORY_GENERAL);
		return false;
	}

	// Body
	output->setBody(pdata + cursor, len - cursor);

	CELL_FREE(data, MEMCATEGORY_GENERAL);

	return true;
}

} // end namespace cell
